package net.qiqbframework.spring.config;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.qiqbframework.modelling.command.Cmd;
import net.qiqbframework.modelling.domain.AggregateRoot;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class ClassPathComponentScanner extends ClassPathBeanDefinitionScanner {

    private final List<Class<? extends Annotation>> managedAnnotationClass = new ArrayList<>();

    @Getter
    private final Map<Class<? extends Annotation>, Map<String, AbstractBeanDefinition>> managedAnnotation = new HashMap<>();


    public ClassPathComponentScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
        managedAnnotationClass.add(AggregateRoot.class);
        managedAnnotationClass.add(Cmd.class);
        registerFilters();
    }

    private void registerFilters() {
        for (Class<? extends Annotation> annotationClass : managedAnnotationClass) {
            includeFilters.add(new AnnotationTypeFilter(annotationClass));
        }
    }


    @Override
    protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
        if (super.checkCandidate(beanName, beanDefinition)) {
            // 自定义 annotationClass 的几个注解类不需要注册 spring 内
            if (beanDefinition instanceof AbstractBeanDefinition scannedGenericBeanDefinition) {
                final String beanClassName = scannedGenericBeanDefinition.getBeanClassName();
                if (beanClassName == null) {
                    return false;
                }
                final Class<?> beanClass;
                try {
                    beanClass = scannedGenericBeanDefinition.resolveBeanClass(this.getClass().getClassLoader());
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
                assert beanClass != null;
                for (Class<? extends Annotation> annotationClass : managedAnnotationClass) {

                    final Annotation annotation = AnnotationUtils.getAnnotation(beanClass, annotationClass);
                    if (annotation != null) {
                        managedAnnotation.computeIfAbsent(annotationClass, k -> new HashMap<>());
                        managedAnnotation.get(annotationClass).put(beanClassName, scannedGenericBeanDefinition);
                    }

                }
                return false;
            }
            return false;
        } else {
            final String beanClassName = beanDefinition.getBeanClassName();
            if (beanClassName == null) {
                return false;
            }
            return false;
        }

    }

    private final List<TypeFilter> includeFilters = new ArrayList<>();

    @Override
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {

        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return true;
    }


}
